﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;
using Charts.QuickChart.Data;
using Client.App.Helpers;
using Client.App.Services;
using Client.App.ViewModels.Interfaces;
using Multiprocessing.Analyse;
using MessageBox = Xceed.Wpf.Toolkit.MessageBox;

namespace Client.App.ViewModels.Implementations
{
    public class EfficiencyAccelerationViewModel : BaseTestViewModel, IEfficiencyAccelerationTestViewModel
    {
        #region Fields

        private string _efficiencyHorizontalTitle;
        private string _efficiencyVerticalTitle;
        private IDataPointCollection<double, double> _efficiency;
        private string _accelerationHorizontalTitle;
        private string _accelerationVerticalTitle;
        private IDataPointCollection<double, double> _acceleration;
        private int _size;
        private int _maxProcessingBlockSize;
        private int _processingBlockSize;
        private IDataPointCollection<double, double> _processorCount;

        private int _attempts;
        private Type _processingBlockType;

        private readonly IConfigurationService _configurationService;
        private ObservableCollection<IUnitStatistics> _iterationStatistics;
        private string _loadingStatus;

        #endregion

        #region Properties

        public IDataPointCollection<double, double> ProcessorCount
        {
            get { return _processorCount; }
            set { SetProperty(ref _processorCount, value, () => ProcessorCount); }
        }

        public ObservableCollection<IUnitStatistics> IterationStatistics
        {
            get { return _iterationStatistics; }
            set { SetProperty(ref _iterationStatistics, value, () => this.IterationStatistics); }
        }

        public int MaxProcessingBlockSize
        {
            get { return _maxProcessingBlockSize; }
            private set { SetProperty(ref _maxProcessingBlockSize, value, () => this.MaxProcessingBlockSize); }
        }

        public int ProcessingBlockSize
        {
            get { return _processingBlockSize; }
            set { SetProperty(ref _processingBlockSize, value, () => this.ProcessingBlockSize); }
        }

        public string EfficiencyHorizontalTitle
        {
            get { return _efficiencyHorizontalTitle; }
            set { SetProperty(ref _efficiencyHorizontalTitle, value, () => this.EfficiencyHorizontalTitle); }
        }

        public string EfficiencyVerticalTitle
        {
            get { return _efficiencyVerticalTitle; }
            set { SetProperty(ref _efficiencyVerticalTitle, value, () => this.EfficiencyVerticalTitle); }
        }

        public string AccelerationHorizontalTitle
        {
            get { return _accelerationHorizontalTitle; }
            set { SetProperty(ref _accelerationHorizontalTitle, value, () => this.AccelerationHorizontalTitle); }
        }

        public string AccelerationVerticalTitle
        {
            get { return _accelerationVerticalTitle; }
            set { SetProperty(ref _accelerationVerticalTitle, value, () => this.AccelerationVerticalTitle); }
        }

        public IDataPointCollection<double, double> Acceleration
        {
            get { return _acceleration; }
            set { SetProperty(ref _acceleration, value, () => this.Acceleration); }
        }

        public IDataPointCollection<double, double> Efficiency
        {
            get { return _efficiency; }
            set { SetProperty(ref _efficiency, value, () => this.Efficiency); }
        }

        public int Size
        {
            get { return _size; }
            set { SetProperty(ref _size, value, () => Size); }
        }

        public string LoadingStatus
        {
            get { return _loadingStatus; }
            set { SetProperty(ref _loadingStatus, value, () => this.LoadingStatus); }
        }

        #endregion

        #region Constructor

        public EfficiencyAccelerationViewModel(Dispatcher dispatcher, IConfigurationService configurationService) 
            : base(dispatcher)
        {
            if (ReferenceEquals(configurationService, null))
            {
                throw new ArgumentNullException("configurationService");
            }
            _configurationService = configurationService;
            _configurationService.MaxProcessingBlockSizeChanged += (s, e) => this.MaxProcessingBlockSize = e.EventInfo;
            _configurationService.ProcessingBlockTypeChanged += (s, e) => this._processingBlockType = e.EventInfo;
            _configurationService.AttemptCountChanged += (s, e) => this._attempts = e.EventInfo;
            
            _maxProcessingBlockSize = _configurationService.GetMaxProcessingBlockSize();
            _processingBlockType = _configurationService.GetProcessingBlockType();
            _attempts = _configurationService.GetAttemptCount();

            _efficiency = new UnorderedDataPointCollection<double, double>();
            _acceleration = new UnorderedDataPointCollection<double, double>();
            _accelerationHorizontalTitle = _efficiencyHorizontalTitle = "Количество юнитов";
            _accelerationVerticalTitle = "Ускорение";
            _efficiencyVerticalTitle = "Эффективность";
            
        }

        #endregion

        #region Methods

        protected override bool CanStartAnalyze()
        {
            return base.CanStartAnalyze() && !IsAnalyzing;
        }

        protected override void OnAnalyze()
        {
            Dispatcher.Invoke(() => IsAnalyzing = true);
            Action action = () =>
                                {
                                    if (ReferenceEquals(IterationStatistics, null))
                                    {
                                        IterationStatistics = new ObservableCollection<IUnitStatistics>();
                                    }
                                    else
                                    {
                                        Dispatcher.Invoke(IterationStatistics.Clear);
                                    }

                                    if (ReferenceEquals(Acceleration, null))
                                    {
                                        Acceleration = new UnorderedDataPointCollection<double, double>();
                                    }
                                    else
                                    {
                                        Dispatcher.Invoke(Acceleration.Clear);
                                    }

                                    if (ReferenceEquals(Efficiency, null))
                                    {
                                        Efficiency = new UnorderedDataPointCollection<double, double>();
                                    }
                                    else
                                    {
                                        Dispatcher.Invoke(Efficiency.Clear);
                                    }

                                    if (ReferenceEquals(ProcessorCount, null))
                                    {
                                        ProcessorCount = new UnorderedDataPointCollection<double, double>();
                                    }
                                    else
                                    {
                                        Dispatcher.Invoke(ProcessorCount.Clear);
                                    }

                                    // draw processors count line
                                    Dispatcher.Invoke(() =>
                                                          {
                                                              ProcessorCount.Add(new DataPoint<double, double>(1,
                                                                                                               Environment
                                                                                                                   .
                                                                                                                   ProcessorCount));
                                                              ProcessorCount.Add(
                                                                  new DataPoint<double, double>(
                                                                      this.ProcessingBlockSize,
                                                                      Environment.ProcessorCount));
                                                          });
                                    // perform calculation
                                    for (int unitCount = 1; unitCount <= this.ProcessingBlockSize; ++unitCount)
                                    {
                                        this.LoadingStatus = string.Format("Выполнение алгоритма на {0} модулях...", unitCount);
                                        IAnalyseUnit analyseUnit = new Acceleration(this.Algorithm);
                                        object[] algorithmParameters = this.Algorithm.CreateParameters(Size);
                                        IList<Tuple<double, TimeSpan>> results = new List<Tuple<double, TimeSpan>>();
                                        for (int i = 0; i < _attempts; ++i)
                                        {
                                            TimeSpan elapsed;
                                            double acceleration = analyseUnit.Calculate(unitCount, _processingBlockType,
                                                                                        out elapsed, algorithmParameters);
                                            results.Add(new Tuple<double, TimeSpan>(acceleration, elapsed));
                                        }

                                        double averageAcceleration = results.Select(t => t.Item1).Average();
                                        TimeSpan averageElapsed = results.Select(t => t.Item2).Average();

                                        double averageEfficiency = averageAcceleration/unitCount;
                                        DataPoint<double, double> accelerationPoint =
                                            new DataPoint<double, double>(unitCount, averageAcceleration),
                                                                  efficiencyPoint = new DataPoint<double, double>(
                                                                      unitCount, averageEfficiency);
                                        IUnitStatistics statistics = new UnitStatistics(unitCount, averageEfficiency,
                                                                                        averageAcceleration,
                                                                                        averageElapsed);
                                        this.Dispatcher.Invoke(() =>
                                                                   {
                                                                       Efficiency.Add(efficiencyPoint);
                                                                       Acceleration.Add(accelerationPoint);
                                                                       IterationStatistics.Add(statistics);
                                                                   });

                                    }
                                };
            action.BeginInvoke(ar =>
                                   {
                                       try
                                       {
                                           action.EndInvoke(ar);
                                       }
                                       catch(Exception ex)
                                       {
                                           Dispatcher.Invoke(() => MessageBox.Show(App.Current.MainWindow, ex.Message, "Ошибка",
                                                           MessageBoxButton.OK, MessageBoxImage.Error));
                                       }

                                       IsAnalyzing = false;
                                       this.LoadingStatus = "Тест закончен";
                                   }, null);
        }
    



        #endregion
    }
}
